//
// Created by cube on 2020/8/23.
//

#ifndef COMPILERDOMEA1_EVENTSYSTEM_H
#define COMPILERDOMEA1_EVENTSYSTEM_H

#include <mutex>
#include <chrono>

#include <queue>

#include "../../Tools.h"
#include "../../Container/ThreadContainer.h"
#include "../Server.h"
#include "Events.h"

namespace Lib{

    template <typename T>
    class TimeOutEventQueue{
    protected:
        struct TimeOutEvent{
            std::chrono::system_clock::time_point timepoint;
            T data;
            friend bool operator <(const TimeOutEvent& a,const TimeOutEvent& b){return a.timepoint < b.timepoint;}
            friend bool operator >(const TimeOutEvent& a,const TimeOutEvent& b){return a.timepoint > b.timepoint;}
        };
    public:
        void send(const std::chrono::microseconds& millis,const T& data){
            std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now();
            TimeOutEvent event;
            event.timepoint = now_time+millis;
            event.data      = data;
            __timeoutQueue.push(event);
        }

        bool exec_one(T& data){
            if(__timeoutQueue.empty())return false;
            const TimeOutEvent& eve = __timeoutQueue.front();
            if(std::chrono::system_clock::now() <= eve.timepoint)return false;
            data = eve.data;
            __timeoutQueue.pop_front();
            return true;
        }

        auto& data(){return __timeoutQueue;}

    private:
        CPriority_Queue<TimeOutEvent,std::greater<TimeOutEvent>>  __timeoutQueue;
    };

    class EventLoop : public Loop<Event*> ,public SingleCase<EventLoop> {
    public:
        struct EventAgent{
        public:
            EventAgent(Event* event)
            :_event(event),__resend(false){}
            void reSendTo(EventLoop* loop){__resend = true;loop->sendEvent(_event);}
            void reSendTo(const std::chrono::microseconds& millis,EventLoop* loop){__resend = true;loop->sendEvent(millis,_event);}
            Event* data(){return _event;}
            Event* operator->(){return _event;}
        protected:
            Event* _event;
            bool __resend;
            friend class EventLoop;
        };

        struct EventHandler{
            using EventAgent = EventLoop::EventAgent;
            EventHandler():__shield_event(false){}
            void setShieldEvent(bool shield){__shield_event = shield;}
            bool shieldEvent(){return __shield_event;}
            bool isUse(){return __use_count != 0;}

        protected:
            virtual bool filter(EventAgent& agent){return false;}
            virtual void event(EventAgent& agent){}

            bool onFilter(EventAgent& agent){
                __use_count++;
                bool rul = filter(agent);
                __use_count--;
                return rul;
            }
            void onEvent(EventAgent& agent){
                __use_count++;
                event(agent);
                __use_count--;
            }

        private:
            std::atomic_bool    __shield_event;
            std::atomic_size_t  __use_count;
            friend class EventLoop;
        };

    public:
        void sendEvent(Event* event);
        void sendEvent(const std::chrono::microseconds& millis,Event* event);
        void exit();

        bool containsHandler(EventHandler* handler){return __handlers.lock()->contains(handler);}
        void addHandler(EventHandler* handler){
            handler->setShieldEvent(false);
            __handlers.lock()->insert(handler);
        }
        void eraseHandler(EventHandler* handler){
            handler->setShieldEvent(true);
            while (handler->__use_count != 0)std::this_thread::yield();
            __handlers.lock()->erase(handler);
        }

        void execAll(){work();}

    protected:
        void init() override;
        void work() override;
        bool filter(Event*& event) override ;
        void action(Event*& event) override ;

    private:
        TMutex<TimeOutEventQueue<Event*>>    __timeout_event_queue;
        TSet<EventHandler*> __handlers;
    };
}

#endif //COMPILERDOMEA1_EVENTSYSTEM_H
